<template>
    <el-dialog v-model="properties.visible" class="smart-uploader" title="文件上传" width="800px"
               :before-close="properties.onClose"
               :close-on-click-modal="false"
               :draggable="true"
               :align-center="true" >

        <!-- 上传主体区域 -->
        <div class="upload-container">
            <el-upload ref="uploaderRef"
                       v-model:file-list="properties.fileList"
                       :auto-upload="false"
                       :class="['drag-area', { 'disabled': isView }]"
                       :disabled="isView"
                       :drag="true"
                       :multiple="true"
                       :on-change="onChange"
                       :http-request="onHttpRequest"
                       :on-progress="onProgress"
                       :on-success="onSuccess"
                       :before-upload="onBeforeUpload"
                       :show-file-list="false">
                <template #trigger>
                    <div class="upload-trigger">
                        <el-icon class="upload-icon">
                            <UploadFilled/>
                        </el-icon>
                        <div class="upload-text">
                            <span class="tip-title">
                                点击选择
                                <el-button link type="primary" @click.stop="properties.onSelected('file')">上传文件</el-button>
                                <el-divider direction="vertical"/>
                                <el-button link type="primary" @click.stop="properties.onSelected('folder')">上传目录</el-button>
                                或 拖拽文件到这里
                            </span>
                            <p class="tip-sub">支持批量上传，单文件最大 2GB</p>
                        </div>
                    </div>
                </template>
            </el-upload>

            <!-- 文件列表 -->
            <el-table :data="properties.fileList" class="file-table" height="45vh">
                <el-table-column label="文件列表" class-name="file-list">
                    <template #header>
                        <div class="action-bar">
                            <div class="action-bar__left"/>
                            <div class="action-bar__right">
                                <el-button type="primary" :icon="Upload" @click="properties.onOperation('upload')">上传</el-button>
                                <el-button type="danger" :icon="Delete" @click="properties.onOperation('clear')">清空</el-button>
                            </div>
                        </div>
                    </template>

                    <el-table-column align="right" fixed="left" header-align="center" label="序 号" type="index" width="70">
                        <template #default="{ $index }">
                            {{ $index + 1 }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="name" label="文件名" min-width="220" show-overflow-tooltip>
                        <template #default="scope">
                            <div class="file-name">
                                <span class="name-text">{{ scope.row.name }}</span>
                            </div>
                        </template>
                    </el-table-column>
                    <el-table-column prop="size" label="大小" width="110">
                        <template #default="scope">
                            {{ sizeFormat(scope.row.size) }}
                        </template>
                    </el-table-column>
                    <el-table-column label="进度" width="200">
                        <template #default="scope">
                            <el-text :type="scope.row.status === 'fail'? 'danger':'primary'">{{properties.onPercentage(scope.row)}}</el-text>
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" width="90" align="center">
                        <template #default="{ row }">
                            <el-button v-if="row.status === 'fail'" type="primary" link :icon="RefreshRight" @click="properties.onOperation('retry', row)"/>
                            <el-button type="danger" link :icon="Delete" @click="properties.onOperation('remove', row)"/>
                        </template>
                    </el-table-column>
                </el-table-column>
            </el-table>
        </div>
    </el-dialog>
</template>

<script setup lang="ts">
import {ref, reactive, onUnmounted, onMounted} from 'vue'
import {UploadFilled, Delete, Upload, RefreshRight} from '@element-plus/icons-vue'

import { mitt, sizeFormat } from '@/utils'
import {useUploadManager} from "@/components/file-upload/chunk-upload-chain";
import {ElMessage, UploadFile, UploadProps, UploadRequestOptions} from "element-plus";

const props = defineProps({
    action: {type: String, default: '/file/upload'},
    global: {type: Boolean, default: false},
    chunkSize: {type: Number, default: 5242880},
    concurrency: {type: Number, default: 3},
})

const uploader =  useUploadManager(props.chunkSize, props.concurrency);

const uploaderRef = ref();
const isView = ref<boolean>(false);
const properties = reactive({
    visible: false,
    fileList: [],
    params: {},
    onClose: () => {
        properties.visible = false;
    },
    onSelected: (type: string) => {
        let item = document.querySelectorAll('.smart-uploader input[name="file"]')[0] as HTMLElement;
        type === 'file' ? item.removeAttribute('webkitdirectory') : item.setAttribute('webkitdirectory', '');
        item.click();
    },
    onOperation: (type: string, file?: any) => {
        switch (type) {
            case 'retry':
                console.warn(`错误文件${file.name}重新上传`);
                uploaderRef.value?.handleRemove(file);
                uploaderRef.value?.handleStart(file.raw);
            case "upload":
                uploaderRef.value?.submit();
                break;
            case "clear":
                uploader.abort();
                uploaderRef.value?.clearFiles()
                break;
            case "remove":
                uploader.abort(file.raw.uid);
                uploaderRef.value?.handleRemove(file);
                break
        }
    },
    onPercentage: (file: any) => {
        switch (file.status) {
            case 'ready':
                return '等待';
            case 'success':
                return '完成';
            case 'fail':
                return `失败(${file.response.message})`;
            case 'uploading':
                if (file.raw.status === 'uploading') {
                    return '正在上传(' + file.percentage + '%)'
                } else if (file.raw.status === 'computing'){
                    return '正在加载(' + file.percentage + '%)'
                } else if (file.raw.status === 'merging') {
                    return '正在合并';
                } else {
                    return '未知'
                }
        }
    }
});

const onChange = (file: any) => {
    // 当文件出错后重新上传时会再次该函数
    if (file.status === 'ready' && !Object.prototype.hasOwnProperty.call(file.raw, 'params')) {
        file.raw.params = {...properties.params}
    }
}

const onBeforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
    if (rawFile.size / 1024 / 1024 / 1024 > 2) {
        ElMessage({message: '单文件最大2GB', grouping: true, type: 'warning'})
        return false
    }
    return true
}

const onHttpRequest = (content: UploadRequestOptions) => {
    uploader.add(props.action, content);
}


const onProgress = (event: any, file: any) => {
    // 细化上传过程（计算MD5,断点校验,上传分块,合并文件）
    file.raw.status = event.status;
}

const onSuccess = (response: any, file: UploadFile) => {
    if (response.code !== 200) {
        file.status = 'fail';
    } else {
        uploaderRef.value?.handleRemove(file);
    }
}

// 展示弹窗
const show = (value: any) => {
    isView.value = value === 'header';
    if (typeof value === 'object'){
        properties.params = value;
    }
    properties.visible = true;
};

onMounted(() => {
    if (props.global) {
        mitt.on('onGlobalFileUpload', (data) => show(data))
    }
})

onUnmounted(() => {
    if (props.global) {
        mitt.off('onGlobalFileUpload')
    }
})

defineExpose({ show })
</script>

<style lang="scss" scoped>
.smart-uploader {
    :deep(.el-dialog__header) {
        margin-right: 0;
        background: linear-gradient(120deg, #f8f9fc, #eef2f6);
        border-radius: 6px 6px 0 0;
    }

    .config-panel {
        margin: -10px 0 12px;
        border: none;

        :deep(.el-collapse-item__header) {
            padding-left: 12px;
            font-size: 13px;
            color: var(--el-color-info);
        }
    }

    .upload-container {
        position: relative;

        .drag-area {
            :deep(.el-upload-dragger) {
                border: 2px dashed var(--el-border-color);
                border-radius: 8px;
                transition: all 0.3s;
                padding: 20px;

                &:hover {
                    border-color: var(--el-color-primary);
                    background: rgba(var(--el-color-primary-rgb), 0.03);
                }
            }

            &.is-dragover {
                transform: scale(0.98);
            }

            .upload-trigger {
                text-align: center;

                .upload-icon {
                    font-size: 48px;
                    color: var(--el-color-primary);
                    margin-bottom: 2px;
                }

                .tip-title {
                    --tip-title-font-size: 16px;
                    font-size: var(--tip-title-font-size);
                    color: var(--el-text-color-primary);
                    margin: 0;

                    :deep(span) {
                        font-size: var(--tip-title-font-size);
                    }

                    .el-button {
                        padding: 4px 6px;
                    }
                }

                .tip-sub {
                    font-size: 12px;
                    color: var(--el-text-color-secondary);
                    margin: 6px 0 0;
                }
            }

            .is-disabled {
                .el-button {
                    cursor: not-allowed;
                }
            }
        }


        .file-table {
            margin-top: 16px;
            border-radius: 6px;

            :deep(.file-list) {
                background: transparent;
            }

            .file-name {
                display: flex;
                align-items: center;
                gap: 8px;

                .name-text {
                    flex: 1;
                }
            }
        }

        .action-bar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin: 0;
        }
    }
}
</style>
